/*
 *      Copyright: Bernd Schubert <bschubert@ddn.com>
 *      License: GPLv3
*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdarg.h>

#include "sd.h"
#include "hash.h"

typedef struct data {
	char fname[4096];
	uint64_t ff_objid;
	uint64_t ff_group;
	uint64_t id;
	uint32_t generation;
	uint32_t f_type;
	uid_t uid;
	gid_t gid;
	time_t timestamp;
	int ost_index;
} ost_fdata_t;


/* This part is a bit tricky. Unfortunately the OST EA data 
 * do NOT store the directory index, but this has to be gathered from 
 * tunefs.lustre. So 'get_filter_fid', which decodes OST EA data, still doesn't 
 * know about the OST index and without the index the data are useless.
 * However, we can create a correlation between the filename and the index
 * using an additional script. E.g. get_filter_fid writes a line
 * 
 * /mnt/tmp/lun__10/O/0/d13/4009933 4009933 0 128653836 2677406724 0 3161 3000
 * 
 * we now have to assign the OST index to lun__10, this is done with the help
 * of tunefs.lustre. The path mapping array will have at index 10 the correspondig OST
 * index, so e.g. 'path_map[10] == 12'. Would be better of course, 
 * if lun__10 would have OST-index 10, but we can't rely on that and for the 
 * present recovery that is also not true. */
static struct mapping {
	int n; 			// number of allocated maps
	int *path_map;	// array of mappings
} mapping;

static void alloc_mapping(unsigned n)
{
	int old = mapping.n;
	int i;
	
	// initialize mappings
	mapping.n += n;
	mapping.path_map = realloc(mapping.path_map, sizeof(int) * mapping.n);
	if (mapping.path_map == NULL) {
		fprintf(stderr, "%s:%d Failed to alloc %d elements\n",
			__func__, __LINE__, mapping.n);
	}
	
	for (i = old; i < mapping.n; i++) {
		mapping.path_map[i] = -1;
	}
}

/**
 * We get e.g. /mnt/tmp/lun__10/O/0/d13/5558317
 * and have to figure out the corresponding OST index from that
 * the index already must be be mapped */
#if 0
static int ost_map_fname_to_index(char *fname)
{
	char f_end[255];
	int lun;
	int res = sscanf(fname, "/mnt/tmp/lun__%d/%s", &lun, f_end);
	
	if (res != 2) {
		fprintf(stderr, "Failed to parse %s: res = %d\n",
			fname, res);
		exit(1);
	} else if (lun > mapping.n || mapping.path_map[lun] == -1) {
		fprintf(stderr, "%s: Unmapped lun %d, aborting\n", 
			fname, lun);
		exit(1);
	} else
		return mapping.path_map[lun];
		
	return -1;
}
#endif

static int ost_map_fname_to_index(char *fname)
{
        char f_end[255];
        int lun;
        int res = sscanf(fname, "/mnt/lustre-oss-0-%*d/OST00%2x/%s", &lun, f_end);
        //int res = sscanf(fname, "/lustre/pfs/ost_%d/%s", &lun, f_end);

        if (res != 2) {
                fprintf(stderr, "Failed to parse %s: res = %d\n",
                        fname, res);
                exit(1);
        }

        return lun;
}

static void add_ost_fid(ost_fdata_t *ost)
{
	static int first = true;
	/* ost_fid size includes lov_user_md_v1 already */
	int size = sizeof(struct ost_fid);
	int stripe_count = ost->f_type + 1;
	struct ost_fid *ost_fid;

	/* see if we have already processed a stripe on this file */
	ost_fid = hash_find_fid(ost->id);
	if (ost_fid == NULL) {
		ost_fid = malloc(size + sizeof(struct lov_user_ost_data_v1) * stripe_count);
		if (ost_fid == NULL) {
			fprintf(stderr, "Malloc of ost_fid failed: %s\n", strerror(errno));
			exit(1);
		}
		ost_fid->uid = ost->uid;
		ost_fid->gid = ost->gid;
		ost_fid->timestamp = ost->timestamp;
		ost_fid->dentry = 0;

		ost_fid->fid.lmm_magic = LOV_USER_MAGIC_V1;
		ost_fid->fid.lmm_pattern = LOV_PATTERN_RAID0;
		ost_fid->fid.lmm_object_id = ost->id;
		ost_fid->fid.lmm_object_gr = 0;
		ost_fid->fid.lmm_stripe_size = 1048576;
		ost_fid->fid.lmm_stripe_count = stripe_count;
		ost_fid->fid.lmm_stripe_offset = 0;
		hash_add_fid(ost_fid);
	} else if (stripe_count > ost_fid->fid.lmm_stripe_count) {
		struct ost_fid *new_fid = realloc(ost_fid, size + sizeof(struct lov_user_ost_data_v1) * stripe_count);
		if (new_fid == NULL) {
			fprintf(stderr, "realloc of ost_fid failed: %s\n", strerror(errno));
			exit(1);
		}

		/* if realloc moves the ptr, update the hash */
		if (new_fid != ost_fid) {
			ost_fid = new_fid;
			hash_update_fid(ost_fid);
		}
	}
	
	if (first) {
		first = false;
		fprintf(stderr, "struct ost_fid size = %d\n", size);
	}

	ost_fid->fid.lmm_objects[ost->f_type].l_object_id = ost->ff_objid;
	ost_fid->fid.lmm_objects[ost->f_type].l_object_gr = ost->ff_group;
	//ost_fid->fid.lmm_objects[ost->f_type].l_ost_gen   = ost->generation;
	ost_fid->fid.lmm_objects[ost->f_type].l_ost_gen   = 0; //FIXME, what is correct?
	ost_fid->fid.lmm_objects[ost->f_type].l_ost_idx   = ost->ost_index;
		
}
	
void read_ost_fid(char *fname)
{
	FILE *file;
	int res;
	ost_fdata_t ost;
	int line = 0;
	int success_lines = 0;


	file = fopen(fname, "r");
	if (file == NULL) {
		fprintf(stderr, "Could not open %s\n", fname);
		exit(1);
	}
	
	do {
		line++;
		res = fscanf(file, "%s %lu %lu %lu %u %u %u %u %lu\n",
				ost.fname, &ost.ff_objid, &ost.ff_group, &ost.id,
				&ost.generation, &ost.f_type, &ost.uid, &ost.gid,
				&ost.timestamp);

		if (res == 9) {
				ost.ost_index = ost_map_fname_to_index(ost.fname);
				add_ost_fid(&ost);
				success_lines++;
		}
				

	} while (res == 9);
	
	if (res != EOF) {
		fprintf(stderr, "%s: Could not get all data in line %d "
			"expected %d, got %d\n", fname, line, 9, res);
		fprintf(stderr, "success_lines %d, error: %s\n", 
			success_lines, strerror(errno));
		exit(1);
	}
}

/**
 * Read mapping which tells, which lun has which OST index
 */
void read_ost_mapping(char *fname)
{
	FILE *file;
	int res;
	int line = 0;
	int success_lines = 0;
	int lun, ost_index;
	
	mapping.n = 0;
	alloc_mapping(1);

	file = fopen(fname, "r");
	if (file == NULL) {
		fprintf(stderr, "Could not open %s\n", fname);
		exit(1);
	}
	
	do {
		line++;
		res = fscanf(file, "lun__%d %d \n", &lun, &ost_index);
		
		if (res == 2) {
			if (mapping.n < lun + 1)
				alloc_mapping(lun + 1 - mapping.n);
			mapping.path_map[lun] = ost_index;
			success_lines++;
		}
	} while (res == 2);
	
	if (res != EOF) {
		fprintf(stderr, "%s: Could not get all data in line %d "
			"expected %d, got %d\n", fname, line, 8, res);
		fprintf(stderr, "success_lines %d, error: %s\n", 
			success_lines, strerror(errno));
		exit(1);
	}
}
